package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * Created with IntelliJ IDEA.
 * Description:
 * User:DELL
 * Date:2025-02-26
 * Time:22:26
 */
public class TcpEchoServer {
    private ServerSocket serverSocket=null;

    public TcpEchoServer(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动！");
        ExecutorService service= Executors.newCachedThreadPool();
        while (true){
            //通过accept方法，把内核中已经建立好的连接拿到应用程序中
            //建立连接的细节流程都是内核自动完成的，应用程序只需要捡现成的
            Socket clientSocket=serverSocket.accept();
            //次数不应该直接用processConnection，会导致服务器不能处理多个客户端
            //创建新的线程来调用更合理的做法，可行，但不够好
//            Thread thread=new Thread(()->{
//                processConnection(clientSocket);
//            });
//            thread.start();
            //更好一点的是用线程池
            service.submit(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
        }
    }
    //通过这个方法，来处理当前的连接
    private void processConnection(Socket clientSocket) {
        //进入方法，先打印一个日志表示当前已经有客户端连接上了
        System.out.printf("[%s:%d] 客户端上线！\n",clientSocket.getInetAddress(),clientSocket.getPort());
        //接下来进行数据的交互
        try (InputStream inputStream=clientSocket.getInputStream();
             OutputStream outputStream= clientSocket.getOutputStream()){
            //使用try方式，避免后序用完了流对象，忘记关闭
            //由于客户端发来的数据可能是多条数据，针对多条数据，就进行循环处理
            while (true){
                Scanner scanner=new Scanner(inputStream);
                if(!scanner.hasNext()){
                    //连接断开了，此时循环就应该结束
                    System.out.printf("[%s:%d] 客户端下线！\n",clientSocket.getInetAddress(),clientSocket.getPort());
                    break;
                }
                //1.读取并解析错误，此处就以next作为读取请求的方式，next的规则是，读到空白符就返回
                String request=scanner.next();
                //2.根据请求，计算响应
                String response=process(request);
                //3.把响应写回到客户端
                //  可以把String转成字节数组，写入到OutputDtream
                //  也可以使用PrintWriter把OutputStream包裹一下，来写入字符串
                PrintWriter printWriter=new PrintWriter(outputStream);
                //此处的println不是打印到控制台了，而是写入到outputStream对应的流对象中，也就是写入到clientSocket
                //自然这个数据也就通过网络发出去了（发给当前这个连接的另一端）
                //此处使用println带有\n也是为了后续客户端这边可以使用scanner,next来读取数据
                printWriter.println(response);
                //此处还要及得有个操作，刷新缓冲区,如果没有刷新操作，可能数据仍然是在内存中，没有被写入网卡
                printWriter.flush();
                //4.打印一下这次请求交互过程中的内容
                System.out.printf("[%s:%d] req=%s,resp=%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),
                        request,response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                //在这个地方，进行clientSocket的关闭
                //processConnection就是在处理一个连接，这个方法执行完毕，这个连接也就处理完了
                clientSocket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public String process(String request) {
        //此处也写的是回显服务器，响应和请求是一样的
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server=new TcpEchoServer(9595);
        server.start();
    }
}
